using System;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

using Nextensions.Security.Cryptography;

namespace Nextensions.GUI
{
	public class InputPersistence
	{
        private const string Keys = "Keys";

        public static void SaveKey(string Key, string Value)
        {
            string path = PathFromKey(Keys);

            if (Regex.IsMatch(Key, @"^\s*$"))
                throw new ArgumentException("Can't pass an empty key");

            StringBuilder file = new StringBuilder();
            string line;

            if (File.Exists(path))
            {
                using (StreamReader reader = new StreamReader(path))
                {
                    while (!reader.EndOfStream)
                    {
                        line = reader.ReadLine().Trim();
                        if (Regex.IsMatch(line, @"^\s*" + Key + @"\s*="))
                            line = Key + "=" + Value;

                        file.AppendLine(line);
                    }
                    reader.Close();
                }
            }

            File.WriteAllText(path, file.ToString());
        }

		public static void SaveUserInput(ComboBox list, string Key)
		{
			string path = PathFromKey(Key);

			if (Regex.IsMatch(list.Text, @"^\s*$"))
				return;

			StringBuilder file = new StringBuilder();
            string val = list.Text;

			if (list.Items.Contains(val))
                list.Items.Remove(val);

			list.Items.Insert(0, val);
            list.SelectedIndex = 0;

            object[] items = new object[list.Items.Count];
            list.Items.CopyTo(items, 0);
            string[] stringItems = Array.ConvertAll<object, string>(items, new Converter<object, string>(ObjToString));
            SaveUserInput(stringItems, Key);
		}

        public static void SaveUserInput(Control Control, string Key)
        {
            SaveUserInput(Control, Key, false);
        }

        public static void SaveUserInput(Control Control, string Key, bool encrypt)
        {
            string path = PathFromKey(Key);
            string text;

            if (Control is CheckBox)
            {
                text = ((CheckBox)Control).Checked ? "1" : "0";
            }
            else if (Control is TabControl)
            {
                text = ((TabControl)Control).SelectedIndex.ToString();
            }
            else
            {
                if (Regex.IsMatch(Control.Text, @"^\s*$"))
                    return;

                text = Control.Text;
            }

            if(encrypt)
                text = Convert.ToBase64String(CryptTool.Encrypt(text, Environment.MachineName));

            SaveUserInput(new string[] { text }, Key);
        }

        private static void SaveUserInput(string[] List, string Key)
        {
            string path = PathFromKey(Key);

            StringBuilder file = new StringBuilder();
            string line;

            if (File.Exists(path))
            {
                StreamReader reader = new StreamReader(path);

                while (!reader.EndOfStream)
                {
                    line = reader.ReadLine().Trim();
                    if (line != "" && line != List[0])
                        file.Append("\r\n" + line);
                }

                reader.Close();
            }

            EnsureDirectory(path);

            StreamWriter writer = new StreamWriter(path);

            writer.Write(List[0]);

            if (file.ToString().Trim() != "")
                writer.Write(file);
            writer.Close();
        }

        public static string LoadKey(string Key)
        {
            string path = PathFromKey(Keys);

            if (!File.Exists(path))
                return null;

            string val = null;

            using (StreamReader reader = new StreamReader(path))
            {
                string line;

                while (!reader.EndOfStream)
                {
                    line = reader.ReadLine().Trim();
                    if (Regex.IsMatch(line, @"^\s*" + Key + @"\s*="))
                    {
                        val = Regex.Match(line, @"=\s*(?<val>.+)$").Groups["val"].Value;
                        break;
                    }
                }
                reader.Close();
            }

            return val;
        }

        public static void LoadUserInput(ComboBox list, string Key, bool AutoLoad)
        {
            LoadUserInput(list, Key);
            if(list.Items.Count > 0)
                list.Text = list.Items[0].ToString();
        }

		public static void LoadUserInput(ComboBox list, string Key)
		{
			string line;
			string path = PathFromKey(Key);

			if (! File.Exists(path)) return;

			StreamReader reader = new StreamReader(path);

			while ((line = reader.ReadLine()) != null)
				list.Items.Add(line);

			reader.Close();
		}

        public static void LoadSingleUserInput(Control Control, string Key)
        {
            LoadSingleUserInput(Control, Key, false);
        }

        public static void LoadSingleUserInput(Control Control, string Key, bool Decrypt)
        {
            string path = PathFromKey(Key);

            if (!File.Exists(path)) return;

            using (StreamReader reader = new StreamReader(path))
            {
                string line = reader.ReadLine();
                
                if (Control is CheckBox)
                {
                    ((CheckBox)Control).Checked = (line == "1");
                }
                else if (Control is TabControl)
                {
                    int index;
                    bool parsed = Int32.TryParse(line, out index);

                    if(parsed)
                        ((TabControl)Control).SelectedIndex = index;
                }
                else
                {
                    if (Decrypt)
                        line = CryptTool.Decrypt(Convert.FromBase64String(line), Environment.MachineName);
                    Control.Text = line;
                }
            
                reader.Close();
            }
        }

        public static void AutoLoadUserInput(Control Form)
        {
            foreach (Control ctrl in Form.Controls)
            {
                // NumbericUpDown controls have textbox children with no names, so we want to avoid those.
                if(String.IsNullOrEmpty(ctrl.Name))
                    continue;

                if (ctrl is ComboBox)
                {
                    LoadUserInput((ComboBox)ctrl, ctrl.Name, true);
                }
                else if (ctrl is TextBox || ctrl is IpInput || ctrl is CheckBox || ctrl is NumericUpDown || ctrl is TabControl)
                {
                    string key = ctrl.Name;
                    bool decrypt = false;

                    if (ctrl is TextBox)
                    {
                        TextBox box = (TextBox)ctrl;

                        // Don't save passwords until we support encryption...
                        if (box.PasswordChar != '\0')
                        {
                            key = ctrl.Name + ".b64";
                            decrypt = true;
                        }
                    }

                    LoadSingleUserInput(ctrl, key, decrypt);
                }

                if (!(ctrl is IpInput))
                    AutoLoadUserInput(ctrl);
            }
        }

        public static void AutoSaveUserInput(Control Form)
        {
            AutoSaveUserInput(Form, null);
        }

        public static void AutoSaveUserInput(Control Form, params Control[] Exclude)
        {
            foreach (Control ctrl in Form.Controls)
            {
                bool exclude = false;

                for (int i = 0; Exclude != null && i < Exclude.Length; i++)
                {
                    if (Exclude[i] == ctrl)
                    {
                        exclude = true;
                        break;
                    }
                }

                if (exclude || String.IsNullOrEmpty(ctrl.Name))
                    continue;

                if (ctrl is ComboBox)
                {
                    if(((ComboBox)ctrl).DropDownStyle == ComboBoxStyle.DropDown)
                        SaveUserInput((ComboBox)ctrl, ctrl.Name);
                }
                else if (ctrl is TextBox || ctrl is NumericUpDown)
                {
                    bool readOnly;
                    char passwordChar = '\0';

                    if(ctrl is TextBox)
                    {
                        readOnly = ((TextBox) ctrl).ReadOnly;
                        passwordChar = ((TextBox) ctrl).PasswordChar;
                    }
                    else
                    {
                        readOnly = ((NumericUpDown) ctrl).ReadOnly;
                    }

                    if (!readOnly)
                    {
                        bool encrypt = false;
                        string key = ctrl.Name;
                            
                        // Encrypt passwords
                        if (passwordChar != '\0')
                        {
                            encrypt = true;
                            key += ".b64";
                        }

                        SaveUserInput(ctrl, key, encrypt);
                    }
                }
                else if (ctrl is IpInput || ctrl is CheckBox || ctrl is TabControl)
                {
                    SaveUserInput(ctrl, ctrl.Name);

                    if (ctrl is TabControl)
                    {
                        AutoSaveUserInput(ctrl, Exclude);
                    }
                }

                if(!(ctrl is IpInput))
                    AutoSaveUserInput(ctrl);
            }
        }

		public static void EnsureDirectory(string path)
		{
			if (!Directory.Exists(Directory.GetParent(path).FullName))
				Directory.CreateDirectory(Directory.GetParent(path).FullName);
		}

		private static string PathFromKey(string Key)
		{
            return String.Format("{0}.{1}.txt", Path.GetFileName(Application.ExecutablePath), Key);

			//string path =
			//	Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName, Key) + ".txt";

			//return path;
		}

        private static string ObjToString(object Obj)
        {
            return Obj.ToString();
        }
	}
}